﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Dispatcher;

namespace Sharp.Infrastructure.WCF
{
    /// <summary>
    /// 消息接收器
    /// </summary>
    [DebuggerStepThrough]
    public class ContextReceiver : ICallContextInitializer
    {
        private static readonly ConcurrentDictionary<string, Dictionary<string, bool>> dic =
            new ConcurrentDictionary<string, Dictionary<string, bool>>();
        public void AfterInvoke(object correlationState) { }
        public object BeforeInvoke(InstanceContext instanceContext, IClientChannel channel, Message message)
        {
            if (message.Headers.FindHeader(ContactContext.ContextHeaderLocalName,
                ContactContext.ContextHeaderNamespace) <= 0)
            {
                return null;
            }
            var context = message.Headers.GetHeader<ContactContext>(ContactContext.ContextHeaderLocalName, ContactContext.ContextHeaderNamespace);

            ContactContext.Current = context;
            if (ContactContext.Current != null && ContactContext.Current.UserId != 0)
            {
                return ContactContext.Current;
            }
            else
            {
                #region 收集、检测可匿名访问的方法
                var actions = message.Headers.Action.Split(new[] { '/' }).Skip(3).Take(2).ToArray();

                var actionDic = dic.GetOrAdd(actions[0], x =>
                  {
                      var filepath = Assembly.GetExecutingAssembly().CodeBase.Replace("file:///", "");
                      var filepaths = Directory
                          .GetFiles(Path.GetDirectoryName(filepath) ?? throw new InvalidOperationException(), "*ServiceContracts*dll").ToList();
                      Dictionary<string, bool> tags = new Dictionary<string, bool>();
                      filepaths.ForEach(file =>
                      {
                          Type[] types;
                          try
                          {
                              byte[] fileData = File.ReadAllBytes(file);
                              var assembly = Assembly.Load(fileData);

                              types = assembly.GetTypes();
                          }
                          catch
                          {
                              return;
                          }
                          foreach (var t in types)
                          {
                              if (t.Name != actions[0]) continue;

                              foreach (var m in t.GetMembers())
                              {
                                  tags.Add(m.Name, m.GetCustomAttributes(false).Any(c => c.GetType().Name == "AllowAnonymousAttribute"));
                              }
                          }

                      });

                      return tags;
                  });

                #endregion

                if (actionDic.TryGetValue(actions[1], out bool res) && res)
                {
                    return ContactContext.Current;
                }
                else
                {
                    throw new Exception("用户未登录");
                }
            }
        }
    }
}
